/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package org.latestbit.smartobjects;

import static org.junit.Assert.*;

import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.Test;
import org.latestbit.smartobjects.IServerSkeleton.ThreadModel;
import org.latestbit.smartobjects.exceptions.SmartBusSystemException;
import org.latestbit.smartobjects.network.EndpointLocation;
import org.latestbit.smartobjects.testmodel.*;

public class SimpleInterfaceTest {
	
	@Test
	public void testSmartObjectSyncServer() throws Exception {
		ISmartBus bus = new SmartBus();
		final String location = "tcp://localhost:7777";
		
		SimpleInterfaceSyncSkeleton simpleSyncServer = new SimpleInterfaceSyncSkeleton (
				new SimpleInterfaceSyncServer()
		);
		simpleSyncServer.setThreadModel(ThreadModel.PER_CLIENT_MULTI_THREAD_MODEL);
		simpleSyncServer.publish ( bus, new EndpointLocation(bus, location) );
		
		testClient ( bus, location );
		
		simpleSyncServer.destroy();
	}
	
	@Test
	public void testSmartObjectAsyncServer() throws Exception {
		ISmartBus bus = new SmartBus();
		final String location = "tcp://localhost:7777";
		
		SimpleInterfaceSkeleton simpleAsyncServer = new SimpleInterfaceSkeleton (
				new SimpleInterfaceAsyncServer()
		);
		simpleAsyncServer.setThreadModel(ThreadModel.PER_CLIENT_MULTI_THREAD_MODEL);
		simpleAsyncServer.publish ( bus, new EndpointLocation(bus, location) );
		
		testClient ( bus, location );
		
		simpleAsyncServer.destroy();
	}	
	
	protected void testClient(ISmartBus bus, String location) throws Exception {
		SimpleInterface simpleClient = new SimpleInterface ();
		simpleClient.resolve( bus, new EndpointLocation(bus, location));
		
		String result = simpleClient.helloWorld("World");
		assertEquals ( "Hello World", result );
		
		ISimpleStructure simpleStructure = new SimpleStructure();
		simpleStructure.setStrVal ( "World2" );
		simpleStructure.setIntVal(BigInteger.ONE);
		result = simpleClient.helloWorld2 ("World", simpleStructure );
		assertEquals ( "Hello World", result );

		simpleStructure = simpleClient.helloWorld3 ("World", simpleStructure );
		assertEquals ( "Hello World", simpleStructure.getStrVal() );

		// Coding with default symmetric algorithm
		
		result = simpleClient.helloWorldEncrypted("World");
		assertEquals ( "Hello World", result );
		
		// Async request
		final Lock asyncConditionLock = new ReentrantLock();
		final Condition asyncCondition = asyncConditionLock.newCondition();
		asyncConditionLock.lock();
		simpleClient.helloWorldRequest("World", 
				new ISimpleInterfaceCallback() {

					@Override
					public void onHelloWorldResponse(String value, Object userObject) {
						assertEquals( "Hello World", value );
						asyncConditionLock.lock();
						asyncCondition.signal();
						asyncConditionLock.unlock();						
					}
					
					@Override
					public void onHelloWorldEncryptedResponse(String value,	Object userObject) {
						onHelloWorldResponse (value, userObject);
					}					

					@Override public void onHelloWorld2Response(String value, Object userObject) {}
					@Override public void onHelloWorld3Response(ISimpleStructure value, Object userObject) {}
					@Override public void onHelloWorld4Response(String value, Object userObject) {}
					@Override public void onHelloWorld4Response(SimpleException ex, Object userObject) {}
					@Override public void onManualDestroyResponse(Object userObject) {}
					
					
					@Override public void onResponseSystemException(String operationId, SmartBusSystemException ex, Object userObject) {}
					@Override public void onResponseTimeout(String operationId, Object userObject) {}
			
				}, 
				simpleStructure
		);
		
		if(!asyncCondition.await(5, TimeUnit.SECONDS))
			throw new TimeoutException("Timeout occured for waiting event");
		asyncConditionLock.unlock();
		
		// Async callback 2 (for method only)
		asyncConditionLock.lock();
		simpleClient.helloWorldRequest("World", 
				new ISimpleInterfaceCallback.ISimpleInterfaceHelloWorldCallback() {
					
					@Override
					public void onResponseTimeout(String operationId, Object userObject) {
					}
					
					@Override
					public void onResponseSystemException(String operationId,
							SmartBusSystemException ex, Object userObject) {
					}
					
					@Override
					public void onHelloWorldResponse(String value, Object userObject) {
						assertEquals( "Hello World", value );
						asyncConditionLock.lock();
						asyncCondition.signal();
						asyncConditionLock.unlock();						
					}
				}, 
				simpleStructure
		);
		
		if(!asyncCondition.await(5, TimeUnit.SECONDS))
			throw new TimeoutException("Timeout occured for waiting event");
		asyncConditionLock.unlock();		
		
		simpleClient.destroy();		
	}
}
